package com.fary.security.config.annotation.web.configuration;

import com.fary.beans.factory.BeanClassLoaderAware;
import com.fary.beans.factory.annotation.Autowired;
import com.fary.beans.factory.annotation.Value;
import com.fary.beans.factory.config.BeanFactoryPostProcessor;
import com.fary.beans.factory.config.ConfigurableListableBeanFactory;
import com.fary.context.annotation.Bean;
import com.fary.context.annotation.Configuration;
import com.fary.context.annotation.DependsOn;
import com.fary.context.annotation.ImportAware;
import com.fary.core.OrderComparator;
import com.fary.core.Ordered;
import com.fary.core.annotation.AnnotationAttributes;
import com.fary.core.annotation.AnnotationUtils;
import com.fary.core.annotation.Order;
import com.fary.core.type.AnnotationMetadata;
import com.fary.security.access.expression.SecurityExpressionHandler;
import com.fary.security.config.annotation.ObjectPostProcessor;
import com.fary.security.config.annotation.SecurityConfigurer;
import com.fary.security.config.annotation.web.builders.WebSecurity;
import com.fary.security.config.crypto.RsaKeyConversionServicePostProcessor;
import com.fary.security.context.DelegatingApplicationListener;
import com.fary.security.web.FilterInvocation;
import com.fary.security.web.access.WebInvocationPrivilegeEvaluator;
import com.fary.security.web.context.AbstractSecurityWebApplicationInitializer;

import javax.servlet.Filter;
import java.util.List;
import java.util.Map;

@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration implements ImportAware, BeanClassLoaderAware {

    // SpringSecurity的FilterChainProxy的建造器
    private WebSecurity webSecurity;

    // 标识是否开启debug模式，来自注解@EnableWebSecurity的属性debug
    private Boolean debugEnabled;

    // SpringSecurity的配置类列表
    private List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers;

    private ClassLoader beanClassLoader;

    @Autowired(required = false)
    private ObjectPostProcessor<Object> objectObjectPostProcessor;

    /**
     * 一个事件监听器的管理器，用于管理所有的鉴权事件
     */
    @Bean
    public static DelegatingApplicationListener delegatingApplicationListener() {
        return new DelegatingApplicationListener();
    }

    /**
     * 权限表达式的支持
     */
    @Bean
    @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public SecurityExpressionHandler<FilterInvocation> webSecurityExpressionHandler() {
        return webSecurity.getExpressionHandler();
    }

    /**
     * 创建FilterChainProxy代理
     * Creates the Spring Security Filter Chain
     */
    @Bean(name = AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public Filter springSecurityFilterChain() throws Exception {
        // 1、判断是否有自定义配置类
        boolean hasConfigurers = webSecurityConfigurers != null && !webSecurityConfigurers.isEmpty();
        // 2、如果没有配置类，创建一个默认配置，并添加到webSecurity
        if (!hasConfigurers) {
            WebSecurityConfigurerAdapter adapter = objectObjectPostProcessor.postProcess(new WebSecurityConfigurerAdapter() {
            });
            webSecurity.apply(adapter);
        }
        return webSecurity.build();
    }

    /**
     * 权限评估器。对于JSP-tag支持是必须的
     */
    @Bean
    @DependsOn(AbstractSecurityWebApplicationInitializer.DEFAULT_FILTER_NAME)
    public WebInvocationPrivilegeEvaluator privilegeEvaluator() {
        return webSecurity.getPrivilegeEvaluator();
    }

    /**
     * 获取并设置容器中已经加载的所有WebSecurityConfigurer实例用于配置，初始化一个WebSecurity
     * Sets the {@code <SecurityConfigurer<FilterChainProxy, WebSecurityBuilder>}
     * instances used to create the web configuration.
     */
    @Autowired(required = false)
    public void setFilterChainProxySecurityConfigurer(ObjectPostProcessor<Object> objectPostProcessor,
            @Value("#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}") List<SecurityConfigurer<Filter, WebSecurity>> webSecurityConfigurers) throws Exception {
        // 1、初始化一个WebSecurity实例，并其赋值到类属性webSecurity上
        webSecurity = objectPostProcessor.postProcess(new WebSecurity(objectPostProcessor));
        // 2、设置是否开启debug模式
        if (debugEnabled != null) {
            webSecurity.debug(debugEnabled);
        }
        // 3、根据@Order注解排序，然后检测配置排序是否有重复
        webSecurityConfigurers.sort(AnnotationAwareOrderComparator.INSTANCE);

        Integer previousOrder = null;
        Object previousConfig = null;
        for (SecurityConfigurer<Filter, WebSecurity> config : webSecurityConfigurers) {
            // AnnotationAwareOrderComparator是本类中的一个内部类
            Integer order = AnnotationAwareOrderComparator.lookupOrder(config);
            if (previousOrder != null && previousOrder.equals(order)) {
                throw new IllegalStateException("@Order on WebSecurityConfigurers must be unique. Order of " + order + " was already used on " + previousConfig + ", so it cannot be used on " + config + " too.");
            }
            previousOrder = order;
            previousConfig = config;
        }
        // 4、将配置添加到webSecurity中
        for (SecurityConfigurer<Filter, WebSecurity> webSecurityConfigurer : webSecurityConfigurers) {
            webSecurity.apply(webSecurityConfigurer);
        }
        // 5、将配置类列表复制到类属性webSecurityConfigurers上
        this.webSecurityConfigurers = webSecurityConfigurers;
    }

    /**
     * 钩子接口，用于修改BeanFactory
     */
    @Bean
    public static BeanFactoryPostProcessor conversionServicePostProcessor() {
        return new RsaKeyConversionServicePostProcessor();
    }

    /**
     * 初始化一个AutowiredWebSecurityConfigurersIgnoreParents实例，用于加载容器中的所有WebSecurityConfigurer实例
     */
    @Bean
    public static AutowiredWebSecurityConfigurersIgnoreParents autowiredWebSecurityConfigurersIgnoreParents(ConfigurableListableBeanFactory beanFactory) {
        return new AutowiredWebSecurityConfigurersIgnoreParents(beanFactory);
    }

    /**
     * 获取类上的Order注解的值
     */
    private static class AnnotationAwareOrderComparator extends OrderComparator {
        private static final AnnotationAwareOrderComparator INSTANCE = new AnnotationAwareOrderComparator();

        @Override
        protected int getOrder(Object obj) {
            return lookupOrder(obj);
        }

        private static int lookupOrder(Object obj) {
            if (obj instanceof Ordered) {
                return ((Ordered) obj).getOrder();
            }
            if (obj != null) {
                Class<?> clazz = (obj instanceof Class ? (Class<?>) obj : obj.getClass());
                Order order = AnnotationUtils.findAnnotation(clazz, Order.class);
                if (order != null) {
                    return order.value();
                }
            }
            return Ordered.LOWEST_PRECEDENCE;
        }
    }

    /**
     * 获取注解的日志级别并设置(ImportAware接口的方法)
     */
    public void setImportMetadata(AnnotationMetadata importMetadata) {
        Map<String, Object> enableWebSecurityAttrMap = importMetadata.getAnnotationAttributes(EnableWebSecurity.class.getName());
        AnnotationAttributes enableWebSecurityAttrs = AnnotationAttributes.fromMap(enableWebSecurityAttrMap);
        debugEnabled = enableWebSecurityAttrs.getBoolean("debug");
        if (webSecurity != null) {
            webSecurity.debug(debugEnabled);
        }
    }

    /**
     * 设置类加载器（BeanClassLoaderAware接口的方法）
     */
    public void setBeanClassLoader(ClassLoader classLoader) {
        this.beanClassLoader = classLoader;
    }
}